1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.math.vector; 12 import core.math : sqrt, sin, cos; 13 float abc(int a, int b) { return cast(float)a+b;} 14 15 struct Vector(uint N, T) 16 { 17 @"format" string toString() 18 { 19 static if(N == 2) return "<$x, $y>"; 20 else static if(N == 3) return "<$x, $y, $z>"; 21 else static if(N == 4) return "<$x, $y, $z, $w>"; 22 } 23 static assert(N >= 2 && N <= 4, "Vector is only implemented for 2, 3 and 4 dimensions"); 24 private alias VectorN = Vector!(N, T); 25 @nogc @safe nothrow pragma(inline, true) 26 { 27 static if(N == 2) 28 { 29 this(T x, T y) 30 { 31 static if(isSIMD) 32 { 33 this.x = x; 34 this.y = y; 35 } 36 else 37 data = [x, y]; 38 } 39 this(T[2] v) 40 { 41 static if(isSIMD) 42 { 43 x = v[0]; 44 y = v[1]; 45 } 46 else 47 data = [v[0], v[1]]; 48 } 49 } 50 else static if (N == 3) 51 { 52 this(T x, T y, T z) 53 { 54 static if(isSIMD) 55 { 56 this.x = x; 57 this.y = y; 58 this.z = z; 59 } 60 else 61 data = [x, y, z]; 62 } 63 this(T[3] v) 64 { 65 static if(isSIMD) 66 { 67 x = v[0]; 68 y = v[1]; 69 z = v[2]; 70 } 71 else 72 data = [v[0], v[1], v[2]]; 73 } 74 } 75 else static if (N == 4) 76 { 77 this(T x, T y, T z, T w = 1) 78 { 79 static if(isSIMD) 80 { 81 this.x = x; 82 this.y = y; 83 this.z = z; 84 this.w = w; 85 } 86 else 87 data = [x, y, z, w]; 88 } 89 this(T[4] v) 90 { 91 static if(isSIMD) 92 { 93 x = v[0]; 94 y = v[1]; 95 z = v[2]; 96 w = v[3]; 97 } 98 else 99 data = [v[0], v[1], v[2], v[3]]; 100 } 101 } 102 static if(N >= 3) 103 { 104 pragma(inline, true) 105 { 106 Vector2 xy(){return Vector2(x, y);} 107 Vector2 xy(Vector2 v){x = v.x; y = v.y; return v;} 108 Vector2 yx(){return Vector2(y, x);} 109 Vector2 yx(Vector2 v){y = v.x; x = v.y; return yx;} 110 } 111 } 112 static if(N == 4) 113 { 114 pragma(inline, true) 115 { 116 Vector3 xyz(){return Vector3(x, y, z);} 117 Vector3 xyz(Vector3 v){x = v.x; y = v.y;z = v.z; return v;} 118 } 119 } 120 pragma(inline, true) T opIndexUnary(string op)(size_t index) if(op == "-") 121 { 122 assert(index >= 0 && index <= N); 123 return mixin(op ~ "data[",index,"];"); 124 } 125 pragma(inline, true) T[N] opCast() const {return cast(const)data;} 126 auto opUnary(string op)() inout if(op == "-") 127 { 128 static if(N == 2) return Vector!(2, T)(-data[0], -data[1]); 129 static if(N == 3) return Vector!(3, T)(-data[0], -data[1], -data[2]); 130 static if(N == 4) return Vector!(4, T)(-data[0], -data[1], -data[2], -data[3]); 131 } 132 float dot()(auto ref VectorN other) const 133 { 134 float ret = 0; 135 for(int i = 0; i < N; i++) 136 ret+= data[i]*other[i]; 137 return ret; 138 } 139 float mag() const 140 { 141 float ret = 0; 142 for(int i = 0; i < N; i++) 143 ret+= data[i]*data[i]; 144 return sqrt(ret); 145 } 146 float magSquare() const 147 { 148 float ret = 0; 149 for(int i = 0; i < N; i++) 150 ret+= data[i]*data[i]; 151 return ret; 152 } 153 void normalize() 154 { 155 const float m = mag(); 156 if(m != 0) 157 data[]/=m; 158 } 159 160 float distance(VectorN other) 161 { 162 float dx = (other.x-x); 163 dx*=dx; 164 float dy = other.y-y; 165 dy*=dy; 166 167 static if(N >= 3) 168 { 169 float dz = other.z-z; 170 dz*=dz; 171 static if(N == 4) 172 { 173 float dw = other.w - w; 174 dw*=dw; 175 return sqrt(dx+dy+dz+dw); 176 } 177 else 178 return sqrt(dx+dy+dz); 179 } 180 static if(N == 2) 181 return sqrt(dx+dy); 182 } 183 184 VectorN unit() inout 185 { 186 const float m = mag(); 187 if(m != 0) 188 return this / m; 189 return this; 190 } 191 192 VectorN project()(auto ref VectorN reference) inout 193 { 194 auto n = reference.unit; 195 return n * dot(reference); 196 } 197 198 static if(N == 3) 199 { 200 VectorN axisAngle(in VectorN axis, float angle) inout 201 { 202 auto n = axis.unit; 203 auto proj = n* axis.dot(n); 204 auto perpendicular = this - proj; 205 auto rot = perpendicular*cos(angle) + n.cross(perpendicular)*sin(angle); 206 return proj + rot; 207 } 208 209 210 VectorN cross()(auto ref VectorN other) inout 211 { 212 return VectorN(data[1]*other[2] - data[2]-other[1], 213 -(data[0]*other[2]- data[2]*other[0]), 214 data[0]*other[1] - data[1]*other[0]); 215 } 216 } 217 218 static float Dot()(auto ref Vector!N first, auto ref Vector!N second){return first.dot(second);} 219 220 static if(N >= 3) 221 { 222 VectorN rotateZ(float radians) 223 { 224 const float c = cos(radians); 225 const float s = sin(radians); 226 227 static if(N == 3) 228 return VectorN(x*c - y*s, y*c + s*x, z); 229 else 230 return Vector!(N, T)(x*c - y*s, y*c + s*x, z, w); 231 } 232 } 233 234 pragma(inline, true) 235 { 236 VectorN opBinary(string op)(in VectorN rhs) inout if(op == "*" || op == "/" || op == "+" || op == "-") 237 { 238 VectorN ret; 239 for(size_t i = 0; i < N; i++) 240 { 241 ret[i] = mixin("data[i] ", op,"rhs[i]"); 242 version(HipMathSkipNanCheck){} 243 else static if(op == "/" || op == "-") assert(ret[i] == ret[i]); //Check for float.nan 244 } 245 return ret; 246 } 247 VectorN opBinary(string op)(float rhs) inout 248 { 249 VectorN ret; 250 for(size_t i = 0; i < N; i++) 251 { 252 ret[i] = mixin("data[i]", op, "rhs"); 253 version(HipMathSkipNanCheck){} 254 else static if(op == "/" || op == "-") assert(ret[i] == ret[i]); //Check for float.nan 255 } 256 return ret; 257 } 258 259 alias opBinaryRight = opBinary; 260 auto opOpAssign(string op)(VectorN other) return 261 { 262 version(HipMathSkipNanCheck) 263 mixin("data[]",op,"= other.data[];"); 264 else 265 { 266 for(size_t i = 0; i < N; i++) 267 { 268 mixin("data[i]",op,"= other[i];"); 269 static if(op == "/" || op == "-") 270 assert(data[i] == data[i]); //Check for float.nan 271 } 272 } 273 return this; 274 } 275 276 auto opOpAssign(string op)(float value) return 277 { 278 version(HipMathSkipNanCheck) 279 mixin("data[]",op,"= value;"); 280 else 281 { 282 for(size_t i = 0; i < N; i++) 283 { 284 mixin("data[i]",op,"= value;"); 285 static if(op == "/" || op == "-") 286 assert(data[i] == data[i]); //Check for float.nan 287 } 288 } 289 return this; 290 } 291 ref VectorN opAssign(in T[N] other) return 292 { 293 for(size_t i = 0; i < N; i++) 294 data[i] = other[i]; 295 return this; 296 } 297 298 static enum VectorN zero = VectorN.init; 299 } 300 301 private enum isSIMD = false; 302 static if(isSIMD) 303 { 304 alias TSimd = mixin(T.stringof~"4"); 305 private TSimd data; //int2, float2 are not supported for some reason 306 } 307 else 308 { 309 ///Use 0 init for every type of number, float starts with nan which always involve into setting it to 310 //a reasonable value 311 private T[N] data = 0; 312 } 313 314 pragma(inline, true) 315 { 316 @trusted inout auto ref x() return 317 { 318 static if(isSIMD) 319 return (cast(T*)&data)[0]; 320 else 321 return data[0]; 322 } 323 @trusted inout auto ref y() return 324 { 325 static if(isSIMD) 326 return (cast(T*)&data)[1]; 327 else 328 return data[1]; 329 } 330 static if(N >= 3) 331 { 332 @trusted inout auto ref z() return 333 { 334 static if(isSIMD) 335 return (cast(T*)&data)[2]; 336 else 337 return data[2]; 338 } 339 } 340 static if(N == 4) 341 { 342 @trusted inout auto ref w() return 343 { 344 static if(isSIMD) 345 return (cast(T*)&data)[3]; 346 else 347 return data[3]; 348 } 349 } 350 351 inout auto ref opIndex(size_t index) 352 { 353 return data[index]; 354 } 355 } 356 357 } 358 } 359 360 alias Vector2 = Vector!(2, float); 361 alias Vector3 = Vector!(3, float); 362 alias Vector4 = Vector!(4, float);